namespace GGF
{
    namespace API
    {
        using System;
        using System.Collections.Generic;
        using System.Text;
        using System.Web;
        using System.Diagnostics;

        /// <summary>
        /// Abstract class used as a template for an "action." Class encapsulates the
        /// desired API list/item method, the list/item ID, and all required (and any
        /// optional) parameter/value pairs as specified by the gubb API.
        /// </summary> 
        public abstract class gAction
        {
            #region Fields

            /// <summary>
            /// Type of element that this action acts upon. Should be either
            /// <see cref="GGF.API.gElement.elementType.itemMethod"/> or
            /// <see cref="GGF.API.gElement.elementType.listMethod"/>.
            /// </summary> 
            protected gElement.elementType eType;

            /// <summary>
            /// A standard .NET integer that represents the unique ID for
            /// the list/item that the action acts upon.
            /// </summary> 
            protected int id;

            /// <summary>
            /// A standard .NET integer that represents the method to be
            /// used for this list/item action. Should be a value in either
            /// <see cref="GGF.API.gElement.enumItemMethod"/> or
            /// <see cref="GGF.API.gElement.enumListMethod"/>.
            /// </summary> 
            protected int method;

            /// <summary>
            /// Collection of parameters that the action's method (set by the user)
            /// supports. All values initially null.
            /// </summary> 
            protected Dictionary<int, string> parameters;

            #endregion Fields

            #region Constructors

            internal gAction(int id)
            {
                this.id = id;
                parameters = new Dictionary<int, string>();
            }

            #endregion Constructors

            #region Methods

            /// <summary>
            /// Changes specified parameter to desired value.
            /// </summary>
            /// <param name="param">An integer representing a value in one of the
            /// following enumerations:
            /// <list type="bullet">
            /// <item><see cref="GGF.API.gElement.enumItemParam"/></item>
            /// <item><see cref="GGF.API.gElement.enumListParam"/></item>
            /// </list>
            /// Depending on the desired type of action, the supported (and proper)
            /// enumerated parameter keys (with their respective values) are stored in the
            /// <c>parameters</c> member.
            /// </param>
            /// <param name="value">The desired value of this parameter.</param>
            /// <returns>Nothing.</returns>
            /// <exception cref="System.Collections.Generic.KeyNotFoundException">Rethrown
            /// when user passes an integer that does not correspond to an enumerated
            /// value in the action's relevant enumeration.
            /// </exception>
            public void changeParam(int param, string value)
            {
                string str;
                gElement.elementType paramType;


                if (eType == gElement.elementType.listMethod)
                    paramType = gElement.elementType.listParam;
                else
                    paramType = gElement.elementType.itemParam;

                try
                {
                    str = gElement.getElement(paramType, param);
                }
                catch (KeyNotFoundException e)
                {
                    throw;
                }

                parameters[param] = value;

 
                Debug.WriteLine("Parameter changed for id = " + id, "Action modification");
            }

            /// <summary>
            /// Clears the value of the specified parameter.
            /// </summary>
            /// <param name="param">An integer representing an enumerated
            /// value which represents the desired parameter for this action type.
            /// <see cref="GGF.API.gAction.changeParam" />.</param>
            /// <returns>Nothing.</returns>
            /// <exception cref="System.Collections.Generic.KeyNotFoundException">Rethrown
            /// when user passes an integer that does not correspond to an enumerated
            /// value in the action's relevant enumeration.
            /// </exception>
            public void clearParam(int param)
            {
                string str;
                gElement.elementType paramType;

                if (eType == gElement.elementType.listMethod)
                    paramType = gElement.elementType.listParam;
                else
                    paramType = gElement.elementType.itemParam;

                try
                {
                    str = gElement.getElement(paramType, param);
                }
                catch (KeyNotFoundException e)
                {
                    throw;
                }

                parameters[param] = null;

                Debug.WriteLine("Parameter cleared for id = " + id, "Action modification");
            }

            /// <summary>
            /// Returns an enumerated integer representing the "type" of action.
            /// <remarks>
            /// This method will return an enumerated value from <see cref="GGF.API.gElement.elementType" />. 
            /// Depending on what API object the action deals with, the value will be either
            /// <see cref="GGF.API.gElement.elementType.itemMethod"/> or
            /// <see cref="GGF.API.gElement.elementType.listMethod"/>.
            /// </remarks>
            /// </summary>
            /// <returns>A <see cref="GGF.API.gElement.elementType" /> specifying
            /// what kind of method the action uses: list or item.</returns>
            public gElement.elementType getElementType()
            {
                return eType;
            }

            /// <summary>
            /// Returns an integer representing the id of the list or item this action acts on.
            /// </summary>
            /// <returns>See summary.</returns>
            public int getId()
            {
                return id;
            }

            /// <summary>
            /// Creates and returns the "bottom half" of the URL for the API
            /// beginning with the method name for the action's specified
            /// list/item.
            /// <remarks>
            /// This method creates a portion of the API URL starting with
            /// the method name. Parameter/value pairs are added after the
            /// method name, separated from other parameters by ampersands.
            /// </remarks>
            /// </summary>
            /// <returns>Second half of URL query string starting with
            /// method name.</returns> 
            public string getQueryString()
            {
                StringBuilder queryBuilder = new StringBuilder();
                int paramCount = 0;

                String methodName;
                String paramName;

                methodName = gElement.getElement(eType, method);

                queryBuilder.Append(methodName);

                if ( !(methodName.Equals("delete")) )
                    queryBuilder.Append(".xml");

                queryBuilder.Append("?");
                
                foreach (KeyValuePair<int, string> entry in parameters)
                {
                    if (entry.Value == "")
                        continue;
                    else
                    {
                        paramCount++;

                        if (paramCount > 1)
                            queryBuilder.Append("&");

                        if ( eType == gElement.elementType.listMethod )
                            paramName = gElement.getElement(gElement.elementType.listParam, entry.Key);
                        else
                            paramName = gElement.getElement(gElement.elementType.itemParam, entry.Key);

                        queryBuilder.Append(paramName);
                        queryBuilder.Append("=");
                        queryBuilder.Append(HttpUtility.UrlEncode(entry.Value, Encoding.UTF8));
                    }

                }

                Debug.WriteLine("Query string requested. Value = " + queryBuilder.ToString(), "Query string request");

                return queryBuilder.ToString();
            }

            #endregion Methods
        }

        /// <summary>
        /// Abstract class from which methods to create new actions for lists and items
        /// are derived. Only contains one method signature for a "factory" method.
        /// <see cref="GGF.API.gActionItemCreator" /> or
        /// <see cref="GGF.API.gActionListCreator" />.
        /// </summary> 
        public abstract class gActionCreator
        {
            #region Methods

            /// <summary>
            /// Abstract factory method for creating new actions for items and lists.
            /// Implemented by <see cref="GGF.API.gActionItemCreator" />
            /// and <see cref="GGF.API.gActionListCreator" />, respectively. 
            /// </summary>
            /// <param name="id">An int representing the unique ID of item
            /// or list.</param>
            /// <param name="method">An int representing desired API method for this
            /// action. Depending on the action type, it should be a value contained
            /// in either <see cref="GGF.API.gElement.enumItemMethod" /> or
            /// <see cref="GGF.API.gElement.enumListMethod" />.</param>
            /// <returns>A <see cref="GGF.API.gAction" />, which should be assigned to
            /// a <see cref="GGF.API.gActionItem" /> or
            /// <see cref="GGF.API.gActionList" />.</returns>
            public abstract gAction FactoryMethod(int id, int method);

            #endregion Methods
        }

        /// <summary>
        /// Concrete class deriving from the abstract class <see cref="GGF.API.gAction" />.
        /// Represents an an action for an individual list item. Intended to be returned
        /// from the concrete <see cref="GGF.API.gActionItemCreator" /> class.
        /// The inherited <c>parameters</c> collection member is populated with all
        /// API item parameter names supported by GGF, represented by keys from
        /// <see cref="GGF.API.gElement.enumItemParam" />.
        /// </summary>
        public class gActionItem : gAction
        {
            #region Constructors

            internal gActionItem(int id, gElement.enumItemMethod itemMethod)
                : base(id)
            {
                method = (int)itemMethod;

                eType = gElement.elementType.itemMethod;                

                parameters.Add((int)gElement.enumItemParam.note, "");
                parameters.Add((int)gElement.enumItemParam.details, "");
                parameters.Add((int)gElement.enumItemParam.date_due, "");
                parameters.Add((int)gElement.enumItemParam.priority, "");
                parameters.Add((int)gElement.enumItemParam.list_id, "");

                Debug.WriteLine("New action for item instantiated for item id = " + id, "Action(item) creation");
            }

            #endregion Constructors
        }

        /// <summary>
        /// Concrete class deriving from the abstract class <see cref="GGF.API.gActionCreator" />.
        /// Represents a "factory" for creating an action for an individual list item.
        /// </summary> 
        public class gActionItemCreator : gActionCreator
        {
            #region Methods

            /// <summary>
            /// Factory method for returning an action tailored for tasks on list items.
            /// </summary>
            /// <param name="id">An int representing the unique ID of item.</param>
            /// <param name="itemMethod">An int representing desired API method for this
            /// action. Value checked for validity; exception rethrown if invalid. See below.
            /// </param>
            /// <returns>A <see cref="GGF.API.gActionItem"/> that is
            /// guaranteed to have a value that corresponds to one of the enumerated
            /// values in <see cref="GGF.API.gElement.enumItemMethod" />.
            /// </returns>
            /// <exception cref="System.Collections.Generic.KeyNotFoundException">Rethrown
            /// when user passes an integer that does not correspond to an enumerated
            /// value in the <see cref="GGF.API.gElement.enumItemMethod" /> enumeration.
            /// </exception>
            public override gAction FactoryMethod(int id, int itemMethod)
            {
                gActionItem gai;

                try
                {
                    gai = new gActionItem(id, (gElement.enumItemMethod)itemMethod);
                }
                catch (KeyNotFoundException e)
                {
                    throw;
                }

                Debug.WriteLine("New factory for item actions instantiated", "Factory(item) creation");
                return gai;
            }

            #endregion Methods
        }

        /// <summary>
        /// Concrete class deriving from the abstract class <see cref="GGF.API.gAction" />.
        /// Represents an an action for a user list. Intended to be returned
        /// from the concrete <see cref="GGF.API.gActionListCreator" /> class.
        /// The inherited <c>parameters</c> collection member is populated with all
        /// API list parameter names supported by GGF, represented by keys from
        /// <see cref="GGF.API.gElement.enumListParam" />.
        /// </summary> 
        public class gActionList : gAction
        {
            #region Constructors

            internal gActionList(int id, gElement.enumListMethod listMethod)
                : base(id)
            {
                method = (int)listMethod;

                eType = gElement.elementType.listMethod;

                parameters.Add((int)gElement.enumListParam.name, "");
                parameters.Add((int)gElement.enumListParam.description, "");
                parameters.Add((int)gElement.enumListParam.filter_status, "");

                Debug.WriteLine("New action for list instantiated for list id = " + id, "Action(list) creation");
            }

            #endregion Constructors
        }

        /// <summary>
        /// Concrete class deriving from the abstract class <see cref="GGF.API.gActionCreator" />.
        /// Represents a "factory" for creating an action for a user list.
        /// </summary> 
        public class gActionListCreator : gActionCreator
        {
            #region Methods

            /// <summary>
            /// Factory method for returning an action tailored for tasks on user lists.
            /// </summary>
            /// <param name="id">An int representing the unique ID of list.</param>
            /// <param name="listMethod">An int representing desired API method for this
            /// action. Value checked for validity; exception rethrown if invalid. See below.
            /// </param>
            /// <returns>A <see cref="GGF.API.gActionList"/> that is
            /// guaranteed to have a value that corresponds to one of the enumerated
            /// values in <see cref="GGF.API.gElement.enumListMethod" />.
            /// </returns>
            /// <exception cref="System.Collections.Generic.KeyNotFoundException">Rethrown
            /// when user passes as integer that does not correspond to an enumerated
            /// value in the <see cref="GGF.API.gElement.enumListMethod" /> enumeration.
            /// </exception>            
            public override gAction FactoryMethod(int id, int listMethod)
            {
                gActionList gal;

                try
                {
                    gal = new gActionList(id, (gElement.enumListMethod)listMethod);
                }
                catch (KeyNotFoundException e)
                {
                    throw;
                }

                Debug.WriteLine("New factory for list actions instantiated", "Factory(list) creation");
                return gal;
            }

            #endregion Methods
        }

    }
}